package dipl.algorithm.test.compare;

import dipl.algorithm.impl.Parameters;
import dipl.algorithm.statistics.Statistics;
import dipl.algorithm.impl.ScmpAlgorithmBezier;
import dipl.algorithm.math.curve.BezierCurve;
import dipl.algorithm.math.fp.primitive.Matrix3x3f;
import dipl.algorithm.math.fp.primitive.Point2df;
import dipl.algorithm.math.fp.primitive.op.AffineTransformations2d;
import dipl.algorithm.math.utility.ApfloatUtils;
import dipl.algorithm.utility.convert.ApfloatConversion;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Random;
import org.apfloat.Apfloat;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import static org.junit.Assert.*;

/**
 * Class provides methods to compare the running time of 
 * the subdivision algorihm with and without Supercomposition on random hard input curves.
 * The yap-bounds are not used.
 * The results are printed to a html-file.
 */
@RunWith(Parameterized.class)
public class CompareAlgorithmsHardWithAndWithoutSCMP {

	//
	// CONSTRUCTOR METHODS
	//

	@BeforeClass
	public static void setUpClass() throws Exception {
		// specify parameters
		run 				= 0;
		runsPerData = 100; // number of runs pe data record

		// create statistical data holders
		int numData = Data().size();
		meanRT_SCMP					= new double[numData];
		meanRT_NSCMP				= new double[numData];
		meanSubdDepth_SCMP	= new double[numData];
		meanSubdDepth_NSCMP = new double[numData];
	}

	@AfterClass
	public static void tearDownClass() throws Exception {
		// create and open html file
		String outDir = "statistics/comparison/results/scmp_nscmp_transversal_hard/";
		if( new File( outDir ).exists() == false )
			new File( outDir ).mkdirs();
		SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd_HH-mm-ss" );
		File file = new File( outDir+format.format( new Date() )+".html" );
		BufferedWriter writer = new BufferedWriter( new FileWriter( file ) );
		
		writer.write( "<html><head><style type=\"text/css\">p,td,th { font-size:small;}</style></head><body>" );
		writer.write( "<h3>Vergleich der Laufzeitdaten des generischen Subdivisionsalgorithmus mit und ohne Superkomposition (Schwierige Fälle, die sich transversal überkreuzen).</h3>" );
		writer.write( "<p>Testläufe je Abbruchschranke: <b>"+runsPerData+"</b> Kurvengrad: <b>3</b></p>" );

		writer.write( "<p><b>Ergebnisse:</b><br>(mS...mit Superkomposition, oS...ohne Superkomposition)</p>" );
		writer.write( "<table border=\"1\">" );
		Object[][] data = (Object[][])Data().toArray();
		writer.write( "<thead><tr>" );
		writer.write( "<th><b>Abbruchschranke:</b></th>" );
		writer.write( "<th><b>Laufzeit (oS):</b></th>" );
		writer.write( "<th><b>mST (oS):</b></th>" );
		writer.write( "<th><b>Laufzeit (mS):</b></th>" );
		writer.write( "<th><b>mST (mS):</b></th>" );
		writer.write( "</tr></thead>" );
		writer.write( "<tbody>" );
		DecimalFormat df4 = new DecimalFormat( "0.0000" );
		DecimalFormat df2 = new DecimalFormat( "0.00" );
		for( int i = 0; i < data.length; i++ ) {
			writer.write( "<tr>" );
			writer.write( "<td>"+(Double)data[i][0]+"</td>" );
			writer.write( "<td>"+df4.format( meanRT_NSCMP[i] )+"s</td>" );
			writer.write( "<td>"+df2.format( meanSubdDepth_NSCMP[i] )+"</td>" );
			writer.write( "<td>"+df4.format( meanRT_SCMP[i] )+"s</td>" );
			writer.write( "<td>"+df2.format( meanSubdDepth_SCMP[i] )+"</td>" );
			writer.write( "</tr>" );
		}
		writer.write( "</tbody>" );
		writer.write( "</table>" );
		writer.write( "</body></html>" );
		writer.close();
	}

	/**
	 * Constructs test using given emergency treshold
	 * @param treshold
	 */
	public CompareAlgorithmsHardWithAndWithoutSCMP( double treshold ) {
		this.treshold = ApfloatUtils.ValueOf( treshold );
	}

	//
	// PARAMETERS
	//

 	@Parameterized.Parameters
 	public static Collection<Object[]> Data() {
 		// return tresholds
		return Arrays.asList( new Object[][] {
			{ Double.valueOf( 1e-3 ) },
			{ Double.valueOf( 1e-4 ) },
			{ Double.valueOf( 1e-5 ) },
			{ Double.valueOf( 1e-6 ) },
			{ Double.valueOf( 1e-7 ) },
			{ Double.valueOf( 1e-8 ) },
			{ Double.valueOf( 1e-9 ) },
			{ Double.valueOf( 1e-10 ) },
			{ Double.valueOf( 1e-11 ) },
			{ Double.valueOf( 1e-12 ) },
		});
 	}

	//
	// TEST/COMPARISON METHODS
	//

	/**
	 * Compares generic spline intersection algorithm with and without
	 * supercomposition.
	 */
	@Test
	public void Compare() {
		// parameters
		// collect results
		Statistics[] resSCMP  = new Statistics[runsPerData]; // statistical results with supercomposition method
		Statistics[] resNSCMP = new Statistics[runsPerData]; // statistical results without supercomposition method
		Random rand = new Random();
		ScmpAlgorithmBezier diAlg = new ScmpAlgorithmBezier(); // subdivision algorithm

		try {

			// generate random input curves (hard cases)

			BezierCurve[] c0 = new BezierCurve[runsPerData];
			BezierCurve[] c1 = new BezierCurve[runsPerData];
			for( int i = 0; i < runsPerData; i++ ) {
        Point2df[] controls_f = new Point2df[4];
        controls_f[0] = new Point2df( -1.0+rand.nextDouble(), -1.0+rand.nextDouble() );
        controls_f[1] = new Point2df( -0.5, 0+0.1*rand.nextDouble() );
        controls_f[2] = new Point2df( 0.5, -controls_f[1].y );
        controls_f[3] = new Point2df( -controls_f[0].x, -controls_f[0].y );
        // controls of second curve are rotated versions of first one
        Point2df[] controls_g = new Point2df[controls_f.length];
        Matrix3x3f mRot		= AffineTransformations2d.Rotation( 0.7*Math.PI/2.0 );
        Matrix3x3f mScale	=	AffineTransformations2d.Scaling( 0.6, 0.6 );
        //Matrix3x3_f mTrans	=	AffineTransformations2d_f.Translation( 0.001, 0.001 );
        for( int j = 0; j < controls_f.length; j++ ) {
          controls_g[j] = AffineTransformations2d.Transform( mRot, controls_f[j] );
          AffineTransformations2d.TransformPoint( mScale, controls_g[j] );
          //AffineTransformations2d_f.TransformPoint( mTrans, controls_g[i] );
        }
        c0[i] = new BezierCurve( ApfloatConversion.ConvertPoints( controls_f ) );
        c1[i] = new BezierCurve( ApfloatConversion.ConvertPoints( controls_g ) );
			}

			// start with supercomp.
			for( int i = 0; i < runsPerData; i++ ) {
				diAlg.CalculateIntersections( c0[i], c1[i], Parameters.COLLECT_DATA|Parameters.SUPERCOMPOSITION, treshold, 0 );
				resSCMP[i] = diAlg.Statistics();
			}
			for( int i = 0; i < runsPerData; i++ ) {
				// start without supercomp.
				diAlg.CalculateIntersections( c0[i], c1[i], Parameters.COLLECT_DATA, treshold, 0 );
				resNSCMP[i] = diAlg.Statistics();
			}

			// get statistical results
			meanSubdDepth_SCMP[run] = 0.0; // mean of subdivision test with supercomposition
			meanSubdDepth_NSCMP[run] = 0.0; // mean of subdivision test without supercomposition
			meanRT_SCMP[run] = 0.0;
			meanRT_NSCMP[run] = 0.0;
			for( int i = 0; i < runsPerData; i++ ) {
				Statistics sSCMP = resSCMP[i];
				Statistics sNSCMP = resNSCMP[i];
				meanSubdDepth_SCMP[run] += Math.max( sSCMP.subdivisionDepth_f, sSCMP.subdivisionDepth_g );
				meanSubdDepth_NSCMP[run] += Math.max( sNSCMP.subdivisionDepth_f, sNSCMP.subdivisionDepth_g );
				meanRT_SCMP[run] += (double)(sSCMP.endTimeMS-sSCMP.startTimeMS)/1000.0;
				meanRT_NSCMP[run] += (double)(sNSCMP.endTimeMS-sNSCMP.startTimeMS)/1000.0;
			}
			meanSubdDepth_SCMP[run] /= (double)runsPerData;
			meanSubdDepth_NSCMP[run] /= (double)runsPerData;
			meanRT_SCMP[run] /= (double)runsPerData;
			meanRT_NSCMP[run] /= (double)runsPerData;

		}
		catch( Exception e) {
			e.printStackTrace();
			fail();
		}
		run++;
	}


	//
	// MEMBERS
	//

	/** emergency treshold */
	Apfloat treshold;

	//
	// STATIC MEMBERS
	//

	// current run
	static int run;
	
	static int runsPerData; // number of runs per data
	
	static double[] meanRT_SCMP;
	static double[] meanRT_NSCMP;

	static double[] meanSubdDepth_SCMP; // mean of subdivision test with supercomposition
	static double[] meanSubdDepth_NSCMP; // mean of subdivision test without supercomposition
}